package birdsong.dummy;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.concurrent.LinkedBlockingQueue;

import birdsong.comm.ClientComm;
import birdsong.comm.ClientSideMessage;
import birdsong.comm.ServerMessage;
import birdsong.comm.ServerSideMessage.Type;

public class ClientCommDummy implements ClientComm {

    private static final int SERVER_PORT = 8080;

    private String clientNick;
    private Socket socket;
    private ObjectInputStream in;
    private ObjectOutputStream out;
    private LinkedBlockingQueue<ClientSideMessage> pendingMessagesQueue = new LinkedBlockingQueue<ClientSideMessage>();
    private boolean isConnected;

    /**
     * Creates a ServerMessage informing the server which client has just
     * connected. Creates a socket, and sends a ServerMessage (connect) thru it
     * to the server. Keeps that socket alive while it is not disconnected.
     */

    @Override
    public void connect(String serverAddress, String nickname) {
	clientNick = nickname;
	ServerMessage mensagemConnectar = new ServerMessage(Type.CLIENT_CONNECTED, null, nickname);
	try {
	    socket = new Socket(serverAddress, SERVER_PORT);
	    out = new ObjectOutputStream(socket.getOutputStream());
	    out.writeObject(mensagemConnectar);
	    isConnected = true;

	    ServerConnectionHandlerThread serverConnectionHandlerThread = new ServerConnectionHandlerThread(socket);
	    serverConnectionHandlerThread.setName("Thread Client - " + nickname);
	    serverConnectionHandlerThread.start();
	} catch (UnknownHostException e) {
	    System.err.println("UnknownHostException: " + e.getMessage());
	} catch (IOException e) {
	    System.err.println("IOException: " + e.getMessage());
	}
    }

    /**
     * Verifies if a client is connected or not thru a boolean.
     */

    @Override
    public boolean isConnected() {
	return isConnected;
    }

    /**
     * Creates a ServerMessage informing the server which client has just
     * disconnected. Sends a ServerMessage (disconnect) thru it to the server.
     * Closes the socket.
     */

    @Override
    public void disconnect() {
	
    	ServerMessage mensagemDisconnect = new ServerMessage(Type.CLIENT_DISCONNECTED, null, clientNick);
    	try {
    		out.writeObject(mensagemDisconnect);
    		try {
    			socket.close();
    		} catch (IOException e) {
    			if (!socket.isClosed()) {
    				e.printStackTrace();
    			}
	    }

	    isConnected = false;
	} catch (IOException e) {
	    if (isConnected = true) {
		e.printStackTrace();
	    }
	}

    }

    /**
     * Verifies if a LinkedBlockingQueue of ClientMessages is empty or not.
     */

    @Override
    public boolean hasNextMessage() {
	return !pendingMessagesQueue.isEmpty();
    }

    /**
     * Takes the first message from a LinkedBlockingQueue to be processed by the
     * Client.
     */

    @Override
    public ClientSideMessage getNextMessage() {
	try {
	    return pendingMessagesQueue.take();
	} catch (InterruptedException e) {
	    e.printStackTrace();
	}
	return null;
    }

    /**
     * Sends a BirdsongMessage with a string (message) and the name of the
     * client who sent it, to the server.
     */

    @Override
    public void sendBirdsongMessage(String birdsongMessage) {
    	ServerMessage mensagemBirdsong = new ServerMessage(Type.BIRDSONG_MESSAGE, birdsongMessage, clientNick);

    	try {
    		out.writeObject(mensagemBirdsong);
    	} catch (IOException e) {
    		e.printStackTrace();
    	}

    }

    /**
     * Creates a ServerMessage informing the server what client a certain other
     * client wishes to follow. Creates a socket, and sends a ServerMessage
     * (follow) thru it to the server.
     */

    @Override
    public void follow(String nickname) {
    	ServerMessage mensagemFollow = new ServerMessage(Type.FOLLOW, nickname, clientNick);

	try {
	    out.writeObject(mensagemFollow);
	} catch (IOException e) {
	    System.err.println("IOException: " + e.getMessage());
	}
    }

    /**
     * Creates a ServerMessage informing the server what client a certain other
     * client wishes to unfollow. Creates a socket, and sends a ServerMessage
     * (unfollow) thru it to the server.
     */

    @Override
    public void unfollow(String nickname) {
    	ServerMessage mensagemUnFollow = new ServerMessage(Type.UNFOLLOW, nickname, clientNick);

	try {
	    out.writeObject(mensagemUnFollow);
	} catch (IOException e) {
	    System.err.println("IOException: " + e.getMessage());
	}
    }

    // ////////////////////////////////////////////////////////////////////

    public class ServerConnectionHandlerThread extends Thread {

	private Socket clientSocket;

	ServerConnectionHandlerThread(Socket clientSocket) {
	    this.clientSocket = clientSocket;
	}

	public void run() {

	    try {
		in = new ObjectInputStream(clientSocket.getInputStream());
	    } catch (IOException e) {
		if (!socket.isClosed()) {
		    e.printStackTrace();
		}
	    }

	    while (!socket.isClosed()) {

		try {
		    ClientSideMessage serverMessage = (ClientSideMessage) in.readObject();
		    pendingMessagesQueue.add(serverMessage);
		} catch (ClassNotFoundException e) {
		    // Nao deve acontecer
		    e.printStackTrace();
		} catch (IOException e) {
		    e.printStackTrace();
		}
	    }

	}

    }

}